package com.dgut.lotterySystem.service.impl;

import com.dgut.lotterySystem.common.errorcode.ServiceErrorCodeConstants;
import com.dgut.lotterySystem.common.pojo.exception.ServiceException;
import com.dgut.lotterySystem.common.utils.JacksonUtil;
import com.dgut.lotterySystem.common.utils.RedisUtil;
import com.dgut.lotterySystem.controller.param.ActivityPrizeInfo;
import com.dgut.lotterySystem.controller.param.ActivityUserInfo;
import com.dgut.lotterySystem.controller.param.CreateActivityParam;
import com.dgut.lotterySystem.controller.param.PageParam;
import com.dgut.lotterySystem.dao.dataObject.ActivityDO;
import com.dgut.lotterySystem.dao.dataObject.ActivityPrizeDO;
import com.dgut.lotterySystem.dao.dataObject.ActivityUserDO;
import com.dgut.lotterySystem.dao.dataObject.PrizeDO;
import com.dgut.lotterySystem.dao.mapper.*;
import com.dgut.lotterySystem.service.IActivityService;
import com.dgut.lotterySystem.service.dto.ActivityDetailDTO;
import com.dgut.lotterySystem.service.dto.ActivityInfoDTO;
import com.dgut.lotterySystem.service.dto.CreateActivityDTO;
import com.dgut.lotterySystem.service.dto.PageInfoListDTO;
import com.dgut.lotterySystem.service.enums.ActivityEnum;
import com.dgut.lotterySystem.service.enums.ActivityPrizeEnum;
import com.dgut.lotterySystem.service.enums.ActivityPrizeTierEnum;
import com.dgut.lotterySystem.service.enums.ActivityUserEnum;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.stream.Collectors;

@Service
public class ActivityServiceImpl implements IActivityService {

    @Autowired
    private PrizeMapper prizeMapper;
    @Autowired
    private UserMapper userMapper;
    @Autowired
    private ActivityMapper activityMapper;
    @Autowired
    private ActivityUserMapper activityUserMapper;
    @Autowired
    private ActivityPrizeMapper activityPrizeMapper;

    private static final Logger logger= LoggerFactory.getLogger(ActivityServiceImpl.class);
    @Autowired
    private RedisUtil redisUtil;

    /**
     * 活动前端缓存，为了区别活动
     */
    private final String ACTIVITY_PREFIX="ACTIVITY_";
    /**
     * 活动过期时间
     */
    private final Long ACTIVITY_TIMEOUT= Long.valueOf(3*24*60*60);
    @Override
    @Transactional(rollbackFor = Exception.class)
    public CreateActivityDTO createActivity(CreateActivityParam param) {
        //校验参数
        checkCreateActivityParam(param);

        //保存活动信息
        ActivityDO activityDO=new ActivityDO();
        activityDO.setActivityName(param.getActivityName());
        activityDO.setDescription(param.getDescription());
        activityDO.setStatus(ActivityEnum.RUNNING.name());
        activityMapper.createActivity(activityDO);

        //保存活动用户信息
        List<ActivityUserDO> userDOList=param.getActivityUserList().stream().map(userInfo->{
            ActivityUserDO activityUserDO=new ActivityUserDO();
            activityUserDO.setActivityId(activityDO.getId());
            activityUserDO.setUserId(userInfo.getUserId());
            activityUserDO.setUserName(userInfo.getUserName());
            activityUserDO.setStatus(ActivityUserEnum.INIT.name());
            return activityUserDO;
        }).collect(Collectors.toList());
        activityUserMapper.batchInsert(userDOList);

        //保存活动奖品信息
        List<ActivityPrizeDO> prizeDOList= param.getActivityPrizeList().stream().map(prizeInfo->{
            ActivityPrizeDO activityPrizeDO=new ActivityPrizeDO();
            activityPrizeDO.setActivityId(activityDO.getId());
            activityPrizeDO.setPrizeId(prizeInfo.getPrizeId());
            activityPrizeDO.setPrizeAmount(prizeInfo.getPrizeAmount());
            activityPrizeDO.setPrizeTiers(prizeInfo.getPrizeTiers());
            activityPrizeDO.setStatus(ActivityPrizeEnum.INIT.name());
            return activityPrizeDO;
        }).collect(Collectors.toList());
        activityPrizeMapper.batchInsert(prizeDOList);

        // 保存活动信息到redis
        //需要查找奖品表中的基本数据列表
        List<Long> prizeBaseIds=param.getActivityPrizeList().stream()
                                .map(ActivityPrizeInfo::getPrizeId)
                                .collect(Collectors.toList());
        List<PrizeDO> prizeBaseDOList=prizeMapper.batchSelectByIds(prizeBaseIds);
        //activityId :: ActivityDetailDTO :活动+奖品+用户
        ActivityDetailDTO detailDTO=converterActivityDetailDTO(activityDO,userDOList,prizeDOList,prizeBaseDOList);
        cacheActivity(detailDTO);
        //返回结果
        CreateActivityDTO createActivityDTO=new CreateActivityDTO();
        createActivityDTO.setActivityId(activityDO.getId());
        return createActivityDTO;
    }

    @Override
    public PageInfoListDTO<ActivityInfoDTO> findActivityInfoList(PageParam param){
        Integer total=activityMapper.count();

        List<ActivityDO> activityDOList=activityMapper.findActivityList(param.offset(),param.getPageSize());
        List<ActivityInfoDTO> activityInfoDTOList=activityDOList.stream().map(activityDO -> {
            ActivityInfoDTO activityInfoDTO=new ActivityInfoDTO();
            activityInfoDTO.setActivityId(activityDO.getId());
            activityInfoDTO.setActivityName(activityDO.getActivityName());
            activityInfoDTO.setDescription(activityDO.getDescription());
            activityInfoDTO.setStatus(ActivityEnum.forName(activityDO.getStatus()));
            return activityInfoDTO;
        }).collect(Collectors.toList());

        return new PageInfoListDTO<>(total,activityInfoDTOList);
    }

    /**
     * 获取活动redis详情
     * @param activityId
     * @return
     */
    @Override
    public ActivityDetailDTO getActivityDetail(Long activityId) {
        if(activityId==null){
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_DETAIL_ID_ERROR);
        }
        //查找redis
        ActivityDetailDTO detailDTO=getCacheActivity(activityId);
        if(detailDTO!=null){
           logger.info("从redis中获取到活动详情 activityId="+activityId);
           return detailDTO;
        }
        //库中查找活动信息
        ActivityDO activityDO=activityMapper.findActivityInfoById(activityId);
        if(activityDO==null){
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_DETAIL_ID_ERROR);
        }
        //库充查找活动奖品信息
        List<ActivityPrizeDO> prizeDOList=activityPrizeMapper.batchSelectByActivityId(activityId);
        //库中查找活动用户信息
        List<ActivityUserDO> userDOList=activityUserMapper.batchSelectByActivityId(activityId);
        //库中查找奖品信息
        List<Long> prizeIds=prizeDOList
                            .stream()
                            .map(ActivityPrizeDO::getPrizeId)
                            .collect(Collectors.toList());
        List<PrizeDO> prizeDOS=prizeMapper.batchSelectByIds(prizeIds);
        //整合存入redis
        detailDTO=converterActivityDetailDTO(activityDO,userDOList,prizeDOList,prizeDOS);
        cacheActivity(detailDTO);
        //返回结果
        return detailDTO;
    }

    /**
     * 更新活动缓存
     * @param activityId
     */
    @Override
    public void cacheActivity(Long activityId) {
        if(activityId==null){
            logger.warn("更新活动缓存，活动id为空");
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_ID_IS_NULL);
        }

        //库中查找活动信息
        ActivityDO activityDO=activityMapper.findActivityInfoById(activityId);
        if(activityDO==null){
            logger.warn("更新活动缓存，活动id错误");
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_ID_ERROR);
        }
        //库充查找活动奖品信息
        List<ActivityPrizeDO> prizeDOList=activityPrizeMapper.batchSelectByActivityId(activityId);
        //库中查找活动用户信息
        List<ActivityUserDO> userDOList=activityUserMapper.batchSelectByActivityId(activityId);
        //库中查找奖品信息
        List<Long> prizeIds=prizeDOList
                .stream()
                .map(ActivityPrizeDO::getPrizeId)
                .collect(Collectors.toList());
        List<PrizeDO> prizeDOS=prizeMapper.batchSelectByIds(prizeIds);
        //整合存入redis
        cacheActivity(converterActivityDetailDTO(activityDO,userDOList,prizeDOList,prizeDOS));

    }

    /**
     * 缓存 活动信息进入redis
     * @param detailDTO
     */
    //ACTIVITY_activityId :: ActivityDetailDTO :活动+奖品+用户
    private void cacheActivity(ActivityDetailDTO detailDTO) {
        //KEY : ACTIVITY_activityId
        //value : ActivityDetailDTO(json)
        if(detailDTO==null||detailDTO.getActivityId()==null){
            logger.warn("缓存的活动信息为空");
            return;
        }
        try{
            redisUtil.set(ACTIVITY_PREFIX+detailDTO.getActivityId()
                    , JacksonUtil.writeValueAsString(detailDTO),ACTIVITY_TIMEOUT);
        }catch(Exception e){
            logger.warn("缓存活动信息失败 cacheActivity Key="+ACTIVITY_PREFIX+detailDTO.getActivityId());
            return;
        }

    }

    /**
     * 根据活动id从缓存中获取活动详细信息
     * @param activityId
     * @return
     */
    private ActivityDetailDTO getCacheActivity(Long activityId){
        if(activityId==null){
            logger.warn("获取活动缓存信息，活动id为空");
            return null;
        }
        try{
            String str=redisUtil.get(ACTIVITY_PREFIX+activityId);
            if(!StringUtils.hasText(str)){
                logger.warn("缓存不存在该活动信息，KEY="+ACTIVITY_PREFIX+activityId);
                return null;
            }
            ActivityDetailDTO activityDTO=JacksonUtil.readValue(str,ActivityDetailDTO.class);
            return activityDTO;
        }catch (Exception e){
            logger.warn("缓存中获取活动信息失败，KEY="+ACTIVITY_PREFIX+activityId);
            return null;
        }
    }

    /**
     * 转化表数据成活动详情DTO
     * @param activityDO
     * @param userDOList
     * @param prizeDOList
     * @param prizeBaseDOList
     * @return
     */

    private ActivityDetailDTO converterActivityDetailDTO(ActivityDO activityDO,
                                            List<ActivityUserDO> userDOList,
                                            List<ActivityPrizeDO> prizeDOList,
                                            List<PrizeDO> prizeBaseDOList) {
        ActivityDetailDTO detailDTO=new ActivityDetailDTO();
        //设置活动信息
        detailDTO.setActivityId(activityDO.getId());
        detailDTO.setActivityName(activityDO.getActivityName());
        detailDTO.setDesc(activityDO.getDescription());
        detailDTO.setStatus(ActivityEnum.forName(activityDO.getStatus()));
        //
        List<ActivityDetailDTO.PrizeDTO> prizeDTOList=prizeDOList.stream()
                .map(apDO->{
                    ActivityDetailDTO.PrizeDTO prizeDTO=new ActivityDetailDTO.PrizeDTO();
                    prizeDTO.setId(apDO.getPrizeId());
                    Optional<PrizeDO> optionalPrizeDO= prizeBaseDOList.stream()
                            .filter(prizeDO -> prizeDO.getId().equals(apDO.getPrizeId()))
                            .findFirst();
                    //如果prizeDO为空，不执行，不为空才执行
                    optionalPrizeDO.ifPresent(prizeDO ->{
                        prizeDTO.setName(prizeDO.getName());
                        prizeDTO.setImageUrl(prizeDO.getImageUrl());
                        prizeDTO.setPrice(prizeDO.getPrice());
                        prizeDTO.setDescription(prizeDO.getDescription());
                    });
                    prizeDTO.setPrizeTier(ActivityPrizeTierEnum.forName(apDO.getPrizeTiers()));
                    prizeDTO.setPrizeAmount(apDO.getPrizeAmount());
                    prizeDTO.setStatus(ActivityPrizeEnum.forName(apDO.getStatus()));
                    return prizeDTO;
                }).collect(Collectors.toList());

        detailDTO.setPrizeDTOList(prizeDTOList);

        List<ActivityDetailDTO.UserDTO> userDTOList=userDOList.stream().map(auDO->{
            ActivityDetailDTO.UserDTO userDTO=new ActivityDetailDTO.UserDTO();
            userDTO.setUserId(auDO.getUserId());
            userDTO.setUserName(auDO.getUserName());
            userDTO.setStatus(ActivityUserEnum.forName(auDO.getStatus()));
            return userDTO;
        }).collect(Collectors.toList());
        detailDTO.setUserDTOList(userDTOList);
        return detailDTO;

    }

    /**
     * 校验创建活动参数
     * @param param
     */
    private void checkCreateActivityParam(CreateActivityParam param) {
        if(param==null){
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_ALL_PARAM_NULL);
        }
        //校验活动信息
        if(!StringUtils.hasText(param.getActivityName())||!StringUtils.hasText(param.getDescription())){
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PARAM_NULL);
        }
        //校验用户信息
        List<ActivityUserInfo> userList=param.getActivityUserList();
        for(ActivityUserInfo activityUserInfo:userList){
            if(activityUserInfo.getUserId()==null
                    ||activityUserInfo.getUserId()<=0
                    ||!StringUtils.hasText(activityUserInfo.getUserName())){
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_USER_INFO_ERROR);
            }
        }
        List<Long> userIds=userList.stream()
                                .map(ActivityUserInfo::getUserId)
                                .distinct()
                                .collect(Collectors.toList());

        List<Long> userIdsExists=userMapper.selectIdsExistsByIds(userIds);

        if(CollectionUtils.isEmpty(userIdsExists)){
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_USER_INFO_NOT_EXISTS);
        }
        userIds.forEach(id->{
            if(!userIdsExists.contains(id)){
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_USER_INFO_NOT_EXISTS);
            }
        });
        //校验奖品信息
        List<ActivityPrizeInfo> prizeList=param.getActivityPrizeList();
        for (ActivityPrizeInfo activityPrizeInfo:prizeList) {
            if(activityPrizeInfo.getPrizeId()==null
                    ||activityPrizeInfo.getPrizeId()<=0
                    ||activityPrizeInfo.getPrizeAmount()==null
                    ||activityPrizeInfo.getPrizeAmount()<=0
                    ||!StringUtils.hasText(activityPrizeInfo.getPrizeTiers())){
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_INFO_ERROR);
            }
            //校验奖品等级
            if(ActivityPrizeTierEnum.forName(activityPrizeInfo.getPrizeTiers())==null){
                throw new ServiceException(ServiceErrorCodeConstants.PRIZE_TIERS_ERROR);
            }
        }

        List<Long> prizeIds= prizeList.stream()
                                    .map(ActivityPrizeInfo::getPrizeId)
                                    .distinct()
                                    .collect(Collectors.toList());
        List<Long> prizeIdsExists=prizeMapper.selectIdsByIds(prizeIds);
        if(CollectionUtils.isEmpty(prizeIdsExists)){
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_INFO_NOT_EXISTS);
        }
        prizeIds.forEach(id->{
            if(!prizeIdsExists.contains(id)){
                throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_PRIZE_INFO_NOT_EXISTS);
            }
        });

        //校验用户数量和奖品数量
        int useAmount=param.getActivityUserList().size();
        Long prizeAmount=param.getActivityPrizeList()
                        .stream()
                        .mapToLong(ActivityPrizeInfo::getPrizeAmount).sum();
        if(useAmount<prizeAmount){
            throw new ServiceException(ServiceErrorCodeConstants.ACTIVITY_USER_PRIZE_AMOUNT_ERROR);
        }
    }
}
